let W = null
// 6.封装一个Watcher类
class Watcher {
    // 两个参数，一个是key所在的对象，一个是key
    constructor(obj, key) {
        this.obj = obj
        this.key = key
        // 6.4 当我要获取值的时候，把当前watcher保存在全局变量上
        W = this

        // 6.1拿值
        this.get()

        // 6.6 在get中用完W,就把W给设置回去
        W = null
    }

    // 当组件内new Watcher时就想得到一个值
    // 6.2 从obj上拿数据，会进到响应式的get方法中
    get() {
        // this ==》 watcher实例
        return this.obj[this.key]
    }

    // 6.11 在watcher内部封装一个update方法,当通知我更新的时候,直接调用当前的update方法
    update() {
        console.log(
            "gogogo,有人通知我了,我重新去请求数据是:" +
            this.get() +
            ",然后开始重新渲染视图!!!!"
        );
    }
}

// 5.定义一个Dep类，用来 收集依赖 通知更新
// 当对数据进行响应式操作的时候，实例化一个dep
class Dep {
    constructor() {
        // 每次实例化Dep的时候，就会创建一个空的数组，用来保存所有的watcher
        this.subs = []
    }

    // 依赖
    // 6.5 收集 watcher
    depend(watcher) {
        this.subs.push(watcher)
        console.log('每次访问数据的时候，就把watcher收集到dep.subs这个数组中', this.subs);
    }


    notify() {
        console.log('通知所有的watcher，重新加载数据');
        // 6.9 遍历
        this.subs.forEach(watcher => {

            // 6.10 通知更新
            watcher.update();
        })
    }
}

// 4.对单个数据进行数据劫持
function defineReactive(_data, key, value) {

    // 还是要对数据进行深层次的数据响应式
    // observe函数中已经有判断，这里无需判断
    observe(value)

    // 5.1 每次对一个值进行响应式的时候，实例化一个Dep类
    const dep = new Dep()


    Object.defineProperty(_data, key, {
        get() {
            console.log('正在获取' + key + '的值', '收集所有的依赖项');

            // 5.2当数据被请求的时候，把观察者watcher收集起来放到dep.subs中
            // 6.3第一次拿值，就把watcher收集起来
            // dep.depend(W)
            // 6.7 我们可能不在模板解析的时候(也就是不是Watcher来要数据),我们需要做判断处理
            if (W) {
                dep.depend(W);
            }

            return value
        },
        set(newVal) {
            if (newVal === value) return
            console.log('正在设置' + key + '的值', '通知所有的watcher进行更新');

            value = newVal

            // 5.2当数据被修改的时候，要通知所有dep.subs里的watcher进行更新
            // 6.8 当我的响应式数据被修改的时候,我要通知所有dep.subs里的watcher 进行更新
            dep.notify()

        }
    })
}

// 3.处理发布订阅模式的操作，收集依赖，发布更新
class Observer {
    // 构造器接收的是要被响应式处理的数据
    constructor(_data) {
        // 把数据放在实例上，原型对象上的方法都可以访问到改数据
        this._data = _data

        // 判断_data是数组还是对象
        if (Array.isArray(_data)) {
            this.observeArray(_data)
        } else {
            this.walk(_data)
        }
    }

    // 在原型对象上，封装处理数组的方法
    observeArray(_data) {
        _data.forEach(item => {
            // 把数组的值交给observe重新监听
            observe(item)
        })
    }

    // 在原型对象上，封装处理对象的方法
    walk(_data) {
        // 遍历对象，对单个数据进行响应式操作
        for (let key of Object.keys(_data)) {
            defineReactive(_data, key, _data[key])
        }
    }
}

// 2.封装observe函数，接收_data,遍历_data的值，一个个进行数据劫持
function observe(_data) {
    // 判断是否是对象还是基本数据类型
    // 如果不是一个对象 为true return
    // 如果是一个对象，但_data为null 也是true，return出去
    if (typeof _data !== 'object' || _data === null) return

    // 实例化Observer
    new Observer(_data)
    // // 递归，要判断数据是基本类型值还是引用数据类型值
    // for (let key of Object.keys(_data)) {
    //     defineReactive(_data, key, _data[key])
    // }
}

// 1.封装一个Vue构造函数
function Vue(options) {
    this._data = options.data

    // 数据代理,把_data中的属性取出来，放在vue实例上
    for (let key of Object.keys(this._data)) {
        Object.defineProperty(this, key, {
            get() {
                return this._data[key];
            },
            set(newVal) {
                this._data[key] = newVal;
            },
        });
    }

    // 拿到数据开始进行响应式操作
    observe(this._data)
}

// 实例化Vue
const vm = new Vue({
    data: {
        name: 'laowang',
        count: 0,
        course: {
            type: 'easy',
            name: 'html'
        }
    }
})

console.log(vm)

function huoqu() {
    // console.log(vm.count);
    // 一个组件一个watcher
    // 解析模板，碰到插值语法时，就会创建一个watcher
    //模拟模板解析,每个组件都会实例化一个watcher 然后开始观察数据
    new Watcher(vm, 'count')
}

function shezhi() {
    vm.count = 3
}

huoqu()
shezhi()